/***************************************************************************
    qgsmaptoolcapture.cpp  -  map tool for capturing points, lines, polygons
    ---------------------
    begin                : January 2006
    copyright            : (C) 2006 by Martin Dobias
    email                : wonder.sk at gmail dot com
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "qgsmaptoolcapture.h"

//#include "qgisapp.h"
#include "qgscursors.h"
#include "qgsgeometryvalidator.h"
#include "qgslayertreeview.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmapmouseevent.h"
#include "qgsmaprenderer.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include "qgsvertexmarker.h"

#include <QCursor>
#include <QPixmap>
#include <QMouseEvent>
#include <QStatusBar>

#include "qgis_dev.h"


QgsMapToolCapture::QgsMapToolCapture( QgsMapCanvas* canvas, enum CaptureMode tool )
    : QgsMapToolEdit( canvas )
    , mRubberBand( 0 )
    , mTempRubberBand( 0 )
    , mValidator( 0 )
    , mSnappingMarker( 0 )
{
    mCaptureMode = tool;
    mCadAllowed = true;

    // enable the snapping on mouse move / release
    mSnapOnMove = true;
    mSnapOnRelease = true;
    mSnapOnDoubleClick = false;
    mSnapOnPress = false;

    mCaptureModeFromLayer = tool == CaptureNone;
    mCapturing = false;

    QPixmap mySelectQPixmap = QPixmap( ( const char ** ) capture_point_cursor );
    mCursor = QCursor( mySelectQPixmap, 8, 8 );

    connect( qgis_dev::instance()->layerTreeView(), SIGNAL( currentLayerChanged( QgsMapLayer * ) ),
             this, SLOT( currentLayerChanged( QgsMapLayer * ) ) );
}

QgsMapToolCapture::~QgsMapToolCapture()
{
    delete mSnappingMarker;

    stopCapturing();

    if ( mValidator )
    {
        mValidator->deleteLater();
        mValidator = 0;
    }
}

void QgsMapToolCapture::deactivate()
{
    delete mSnappingMarker;
    mSnappingMarker = 0;

    QgsMapToolEdit::deactivate();
}

void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
{
    if ( !mCaptureModeFromLayer )
    {
        return;
    }

    mCaptureMode = CaptureNone;

    QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
    if ( !vlayer )
    {
        return;
    }

    switch ( vlayer->geometryType() )
    {
    case QGis::Point:
        mCaptureMode = CapturePoint;
        break;
    case QGis::Line:
        mCaptureMode = CaptureLine;
        break;
    case QGis::Polygon:
        mCaptureMode = CapturePolygon;
        break;
    default:
        mCaptureMode = CaptureNone;
        break;
    }
}

void QgsMapToolCapture::canvasMapMoveEvent( QgsMapMouseEvent * e )
{
    bool snapped = e->isSnapped();
    QgsPoint point = e->mapPoint();

    if ( !snapped )
    {
        delete mSnappingMarker;
        mSnappingMarker = 0;
    }
    else
    {
        if ( !mSnappingMarker )
        {
            mSnappingMarker = new QgsVertexMarker( mCanvas );
            mSnappingMarker->setIconType( QgsVertexMarker::ICON_CROSS );
            mSnappingMarker->setColor( Qt::magenta );
            mSnappingMarker->setPenWidth( 3 );
        }
        mSnappingMarker->setCenter( point );
    }

    if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
    {
        mTempRubberBand->movePoint( point );
    }
} // mouseMoveEvent


void QgsMapToolCapture::canvasMapPressEvent( QgsMapMouseEvent* e )
{
    Q_UNUSED( e );
    // nothing to be done
}

int QgsMapToolCapture::nextPoint( const QgsPoint& mapPoint, QgsPoint& layerPoint )
{
    QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
    if ( !vlayer )
    {
        QgsDebugMsg( "no vector layer" );
        return 1;
    }
    try
    {
        layerPoint = toLayerCoordinates( vlayer, mapPoint ); //transform snapped point back to layer crs
    }
    catch ( QgsCsException &cse )
    {
        Q_UNUSED( cse );
        QgsDebugMsg( "transformation to layer coordinate failed" );
        return 2;
    }

    return 0;
}

int QgsMapToolCapture::addVertex( const QgsPoint& point )
{
    if ( mode() == CaptureNone )
    {
        QgsDebugMsg( "invalid capture mode" );
        return 2;
    }

    int res;
    QgsPoint layerPoint;
    res = nextPoint( point, layerPoint );
    if ( res != 0 )
    {
        return res;
    }

    if ( !mRubberBand )
    {
        mRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QGis::Polygon : QGis::Line );
    }
    mRubberBand->addPoint( point );
    mCaptureList.append( layerPoint );

    if ( !mTempRubberBand )
    {
        mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QGis::Polygon : QGis::Line, true );
    }
    else
    {
        mTempRubberBand->reset( mCaptureMode == CapturePolygon ? true : false );
    }
    if ( mCaptureMode == CaptureLine )
    {
        mTempRubberBand->addPoint( point );
    }
    else if ( mCaptureMode == CapturePolygon )
    {
        const QgsPoint *firstPoint = mRubberBand->getPoint( 0, 0 );
        mTempRubberBand->addPoint( *firstPoint );
        mTempRubberBand->movePoint( point );
        mTempRubberBand->addPoint( point );
    }

    validateGeometry();

    return 0;
}


void QgsMapToolCapture::undo()
{
    if ( mRubberBand )
    {
        int rubberBandSize = mRubberBand->numberOfVertices();
        int tempRubberBandSize = mTempRubberBand->numberOfVertices();
        int captureListSize = mCaptureList.size();

        if ( rubberBandSize < 1 || captureListSize < 1 )
        {
            return;
        }

        mRubberBand->removePoint( -1 );

        if ( rubberBandSize > 1 )
        {
            if ( tempRubberBandSize > 1 )
            {
                const QgsPoint *point = mRubberBand->getPoint( 0, rubberBandSize - 2 );
                mTempRubberBand->movePoint( tempRubberBandSize - 2, *point );
            }
        }
        else
        {
            mTempRubberBand->reset( mCaptureMode == CapturePolygon ? true : false );
        }

        mCaptureList.removeLast();

        validateGeometry();
    }
}

void QgsMapToolCapture::canvasKeyPressEvent( QKeyEvent* e )
{
    if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
    {
        undo();

        // Override default shortcut management in MapCanvas
        e->ignore();
    }
    else if ( e->key() == Qt::Key_Escape )
    {
        stopCapturing();

        // Override default shortcut management in MapCanvas
        e->ignore();
    }
}

void QgsMapToolCapture::startCapturing()
{
    mCapturing = true;
}

bool QgsMapToolCapture::isCapturing() const
{
    return mCapturing;
}

void QgsMapToolCapture::stopCapturing()
{
    if ( mRubberBand )
    {
        delete mRubberBand;
        mRubberBand = 0;
    }

    if ( mTempRubberBand )
    {
        delete mTempRubberBand;
        mTempRubberBand = 0;
    }

    while ( !mGeomErrorMarkers.isEmpty() )
    {
        delete mGeomErrorMarkers.takeFirst();
    }

    mGeomErrors.clear();

#ifdef Q_OS_WIN
    // hope your wearing your peril sensitive sunglasses.
//    qgis_dev::instance()->skipNextContextMenuEvent();
#endif

    mCapturing = false;
    mCaptureList.clear();
    mCanvas->refresh();
}

void QgsMapToolCapture::deleteTempRubberBand()
{
    if ( mTempRubberBand )
    {
        delete mTempRubberBand;
        mTempRubberBand = 0;
    }
}

void QgsMapToolCapture::closePolygon()
{
    mCaptureList.append( mCaptureList[0] );
}

void QgsMapToolCapture::validateGeometry()
{
    QSettings settings;
    if ( settings.value( "/qgis/digitizing/validate_geometries", 1 ).toInt() == 0 )
    {
        return;
    }

    if ( mValidator )
    {
        mValidator->deleteLater();
        mValidator = 0;
    }

    mTip = "";
    mGeomErrors.clear();
    while ( !mGeomErrorMarkers.isEmpty() )
    {
        delete mGeomErrorMarkers.takeFirst();
    }

    QgsGeometry *g = 0;

    switch ( mCaptureMode )
    {
    case CaptureNone:
    case CapturePoint:
        return;
    case CaptureLine:
        if ( mCaptureList.size() < 2 )
        {
            return;
        }
        g = QgsGeometry::fromPolyline( mCaptureList.toVector() );
        break;
    case CapturePolygon:
        if ( mCaptureList.size() < 3 )
        {
            return;
        }
        g = QgsGeometry::fromPolygon( QgsPolygon() << ( QgsPolyline() << mCaptureList.toVector() << mCaptureList[0] ) );
        break;
    }

    if ( !g )
    {
        return;
    }

    mValidator = new QgsGeometryValidator( g );
    connect( mValidator, SIGNAL( errorFound( QgsGeometry::Error ) ), this, SLOT( addError( QgsGeometry::Error ) ) );
    connect( mValidator, SIGNAL( finished() ), this, SLOT( validationFinished() ) );
    mValidator->start();

    QStatusBar *sb = qgis_dev::instance()->statusBar();
    sb->showMessage( tr( "Validation started." ) );
    delete g;
}

void QgsMapToolCapture::addError( QgsGeometry::Error e )
{
    mGeomErrors << e;
    QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
    if ( !vlayer )
    {
        return;
    }

    if ( !mTip.isEmpty() )
    {
        mTip += "\n";
    }

    mTip += e.what();

    if ( e.hasWhere() )
    {
        QgsVertexMarker *vm =  new QgsVertexMarker( mCanvas );
        vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
        vm->setIconType( QgsVertexMarker::ICON_X );
        vm->setPenWidth( 2 );
        vm->setToolTip( e.what() );
        vm->setColor( Qt::green );
        vm->setZValue( vm->zValue() + 1 );
        mGeomErrorMarkers << vm;
    }

    QStatusBar *sb = qgis_dev::instance()->statusBar();
    sb->showMessage( e.what() );
    if ( !mTip.isEmpty() )
    {
        sb->setToolTip( mTip );
    }
}

void QgsMapToolCapture::validationFinished()
{
    QStatusBar *sb = qgis_dev::instance()->statusBar();
    sb->showMessage( tr( "Validation finished." ) );
}
